package com.liuwei.msmall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liuwei.msmall.common.dto.SkuInfoDTO;
import com.liuwei.msmall.common.dto.SkuStockTo;
import com.liuwei.msmall.common.dto.mq.OrderTo;
import com.liuwei.msmall.common.dto.mq.StockLockTo;
import com.liuwei.msmall.common.dto.mq.StockTaskDetailTo;
import com.liuwei.msmall.common.enums.OrderStatusEnum;
import com.liuwei.msmall.common.enums.WareTaskStatusEnum;
import com.liuwei.msmall.common.exception.NoStockException;
import com.liuwei.msmall.common.exception.RRException;
import com.liuwei.msmall.common.utils.PageUtils;
import com.liuwei.msmall.common.utils.Query;
import com.liuwei.msmall.common.utils.R;
import com.liuwei.msmall.ware.dao.WareSkuDao;
import com.liuwei.msmall.ware.entity.WareOrderTaskDetailEntity;
import com.liuwei.msmall.ware.entity.WareOrderTaskEntity;
import com.liuwei.msmall.ware.entity.WareSkuEntity;
import com.liuwei.msmall.ware.feign.OrderFeignService;
import com.liuwei.msmall.ware.feign.ProductFeignService;
import com.liuwei.msmall.ware.service.WareOrderTaskDetailService;
import com.liuwei.msmall.ware.service.WareOrderTaskService;
import com.liuwei.msmall.ware.service.WareSkuService;
import com.liuwei.msmall.ware.vo.LockStockVo;
import com.liuwei.msmall.ware.vo.OrderConfirmItemVo;
import com.liuwei.msmall.ware.vo.OrderVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Resource
    ProductFeignService productFeignService;

    @Autowired
    OrderFeignService orderFeignService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;


    @Autowired
    RabbitTemplate rabbitTemplate;



    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        LambdaQueryWrapper<WareSkuEntity> wrapper = new LambdaQueryWrapper<>();
        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");

        wrapper.eq(StringUtils.isNoneBlank(skuId),WareSkuEntity::getSkuId,skuId);
        wrapper.eq(StringUtils.isNoneBlank(wareId),WareSkuEntity::getWareId,wareId);

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void saveInfo(WareSkuEntity wareSku) {

        Long skuId = wareSku.getSkuId();

        R r = productFeignService.info(skuId);
        SkuInfoDTO skuinfo = r.getData("skuInfo", new TypeReference<SkuInfoDTO>() {});
        if (Objects.isNull(skuinfo)) {
            throw new RRException("id"+wareSku.getSkuId()+"对应的商品不存在");
        }
        wareSku.setSkuName(skuinfo.getSkuName());
        this.save(wareSku);
    }

    @Override
    public List<SkuStockTo> getHasStock(List<Long> skuids) {
        if(CollectionUtils.isEmpty(skuids)){
            return null;
        }
        List<SkuStockTo> skuStockTos = skuids.stream().map(skuid -> {
            Long count = this.baseMapper.selectHasStockBySkuId(skuid);
            SkuStockTo skuStockTo = new SkuStockTo();
            skuStockTo.setSkuId(skuid);
            skuStockTo.setHasStock(count==null?false:(count>0));
            skuStockTo.setStock(count==null?0:count);
            return skuStockTo;
        }).collect(Collectors.toList());

        return skuStockTos;
    }

    @Override
    public Boolean lockOrderStock(LockStockVo vo) {


        //一个订单对应一个库存工作单，保存对应的库存工作单
        WareOrderTaskEntity wareOrderTaskEntity=new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(vo.getOrderSn());//记录订单号
        wareOrderTaskService.save(wareOrderTaskEntity);

        List<OrderConfirmItemVo> lockItems = vo.getLockItems();
        if (lockItems==null||lockItems.size()==0){
            return false;
        }
        //遍历所有的商品，只要有一个商品库存不足，则全部商品库存锁定失败，抛出异常，回滚事务
        for (OrderConfirmItemVo lockItem : lockItems) {
            //1.根据skuid查出哪个仓库有对应的商品库存
            List<Long> wareIds = this.getBaseMapper().selectWareIdHasStock(lockItem.getSkuId());
            if (wareIds == null || wareIds.size()==0){
                //这个商品库存为空
                throw new NoStockException(lockItem.getSkuId());
            }
            Boolean stockLocked=false;
            //对仓库进行锁定
            for (Long wareId : wareIds) {
                //todo 可以查找距离收货地址比较近的仓库，
                Long count = this.getBaseMapper().lockWareStock(lockItem.getSkuId(),wareId,lockItem.getCountNum());
                if (count == 1){
                    //1.保存库存工作详情
                    WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity(null,lockItem.getSkuId(),"可以调用服务查询",lockItem.getCountNum(),wareOrderTaskEntity.getId(),wareId, WareTaskStatusEnum.STOCK_LOCKED.getCode());
                    wareOrderTaskDetailService.save(taskDetailEntity);
                    //2.告诉Mq,必须告诉mq库存工作单的详情，不然库存锁定失败回滚后，可以拿到mq的消息进行解锁库存
                    StockLockTo stockLockTo = new StockLockTo();
                    stockLockTo.setWareTaskId(wareOrderTaskEntity.getId());
                    StockTaskDetailTo stockTaskDetailTo = new StockTaskDetailTo();
                    BeanUtils.copyProperties(taskDetailEntity,stockTaskDetailTo);
                    stockLockTo.setDetail(stockTaskDetailTo);
                    //TODO 告诉MQ库存锁定成功
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockTo);
                    stockLocked=true;
                    //锁定成功,直接跳出循环
                    break;
                }
                //锁定库存失败,转移锁定下一个仓库

            }
            if (!stockLocked){
                //说明某个商品的所有仓库库存都不足，回滚所有操作
                throw new NoStockException(lockItem.getSkuId());
            }

        }
        //到这里就说明所有商品库存锁定成功
        return true;
    }

    /**
     * 库存自动解锁逻辑
     * @param stockLockTo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlockStock(StockLockTo stockLockTo) {
        Long wareTaskId = stockLockTo.getWareTaskId();//库存工作单的id
        //去数据库查询，库存工作单
        WareOrderTaskEntity wareOrderTask = wareOrderTaskService.getById(wareTaskId);
        //拿到工作单详情id重新查询最新的工作单详情
        StockTaskDetailTo stockLockToDetail = stockLockTo.getDetail();
        WareOrderTaskDetailEntity detail= wareOrderTaskDetailService.getById(stockLockToDetail.getId());
        if (wareOrderTask!=null) {
            return;
        }
        /**
         * 解锁
         1.查询订单，如果订单查不到，必须解锁
         *  2.订单查到了，看订单状态
         *      1）订单已取消，必须解锁
         *      2）订单未取消，不用解锁
         */
        String orderSn = wareOrderTask.getOrderSn();
        //在远程调用过程中如果fegin出现异常，网络故障，开启手动确认消息，保证了消息不会被丢失，
        R r = orderFeignService.getOrderByOrderSn(orderSn);
        if (r.getCode()==0){
            OrderVo data = r.getData("data", new TypeReference<OrderVo>() {});
            //如果订单为空 或者 订单已取消
            if (data==null || data.getStatus()== OrderStatusEnum.CANCLED.getCode()){
                //判断库存工作单状态是否是已锁定状态，只有已锁定状态才进行解锁
                if (detail.getLockStatus()==WareTaskStatusEnum.STOCK_LOCKED.getCode()){
                    unlockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detail.getId());
                }
            }

        }else {
            throw new RuntimeException("调用订单服务查询失败");
        }
    }

    /**
     *
     * @param skuId
     * @param wareId
     * @param skuNum
     * @param taskDetailId
     */
    private void unlockStock(Long skuId, Long wareId, Integer skuNum, Long taskDetailId) {
        //解锁
        this.getBaseMapper().unlockWareStock(skuId,wareId,skuNum);

        //更新库存工作单
        WareOrderTaskDetailEntity entity = new WareOrderTaskDetailEntity();
        entity.setId(taskDetailId);
        entity.setLockStatus(WareTaskStatusEnum.STOCK_UNLOCKED.getCode());//状态改为已解锁状态
        wareOrderTaskDetailService.updateById(entity);
    }

    /**
     * 处理订单服务主动解锁逻辑
     * @param orderTo
     */
    //订单关闭后，发送的库存解锁消息
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlockStock(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        WareOrderTaskEntity taskEntity = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        if (Objects.nonNull(taskEntity)){
            Long id = taskEntity.getId();
            //从库存工作单详情中查出需要解锁的库存
            List<WareOrderTaskDetailEntity> taskDetailEntities = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().
                    eq("task_id", id).
                    eq("lock_status", WareTaskStatusEnum.STOCK_LOCKED.getCode()));
            if (!CollectionUtils.isEmpty(taskDetailEntities)){
                for (WareOrderTaskDetailEntity taskDetailEntity : taskDetailEntities) {
                    //判断库存工作单状态是否是已锁定状态，只有已锁定状态才进行解锁
                    //Long skuid,long wareId,Integer num,Long taskDetailId
                    unlockStock(taskDetailEntity.getSkuId(),taskDetailEntity.getWareId(),taskDetailEntity.getSkuNum(),taskDetailEntity.getId());
                }
            }
        }

    }

}